﻿Option Explicit On
Module modMain
    <Runtime.InteropServices.DllImport("kernel32.dll")> _
    Public Function GetTickCount() As UInteger ' Имя функции НЕ МЕНЯТЬ
    End Function


    Public Enum AdvState As Integer
        normal = 0      'нормальное
        estateInDB = 1  'риэлтор в БД
        estateNotDB = 2 'риэлтор не в БД
        old = 3         'старая
        bad = 4         'не нужная
        'badPhone = 5    'получен не правильный номер телефона
    End Enum


    Public Structure AdvertType
        Public url As String
        Public advid As String
        Public state As AdvState
        Public street As String
        Public nomer As String
        Public building As String
        Public balkon As String
        Public floor As String
        Public floor_all As String
        Public kvm_all As String
        Public kvm_live As String
        Public kvm_kitchen As String
        Public price As String
        Public phone() As String
        Public phone_count() As Integer
        Public content As String
        Public text As String

        Public Sub AddText(ByVal txt As String, Optional ByVal separator As String = " ")
            If txt = "" Then Exit Sub
            'Если текст не пустой, то добавляем в начало сепаратор
            If Me.text <> "" Then Me.text = Me.text & separator
            'Добавляем в конец новый текст
            Me.text = Me.text & txt
        End Sub

        Public Sub AddContent(ByVal txt As String, Optional ByVal separator As String = " ")
            If txt = "" Then Exit Sub
            'Если текст не пустой, то добавляем в начало сепаратор
            If Me.content <> "" Then Me.content = Me.content & separator
            'Добавляем в конец новый текст
            Me.content = Me.content & txt
        End Sub
    End Structure



    'Возвращает отформатированый номер телефона
    'если выражение не телефон, то возвращает False
    Public Function getBigPhone(ByVal phone As Object) As Object
        'Проверяем, это телефон или нет
        phone = getSmallPhone(phone)

        If phone <> False Then
            'По длине строки определяем определяем тип номера
            'в зависимости от типа применяем свое рег.выражение
            Select Case Len(phone)
                Case 11 'сотовый номер 8-999-123-4567
                    Return Regex.Replace(phone, "(8|7)+(\d{3})+(\d{3})+(\d{4})+", "8-$2-$3-$4")
                Case 10 'сотовый номер 999-123-4567
                    Return Regex.Replace(phone, "(\d{3})+(\d{3})+(\d{4})+", "8-$1-$2-$3")
                Case 6 'домашний номер 12-34-56
                    Return Regex.Replace(phone, "(\d{2})+(\d{2})+(\d{2})+", "$1-$2-$3")
                Case Else
                    Return False
            End Select
        Else
            Return False
        End If
    End Function


    'Возвращает номер телефона без постороних символов (только цифры)
    'если выражение не телефон, то возвращает False
    Public Function getSmallPhone(ByVal phone As String) As Object
        'Чистим строку от не нужных символов
        phone = phone.Replace("+7", "8")
        phone = Regex.Replace(phone, "(-|—|\s|\+|\(|\))", "")
        phone = Regex.Replace(phone, "^(8|7|)4217", "")

        'Проверяем наличие не цифровых символов
        If Regex.IsMatch(phone, "[\D]") Then
            getSmallPhone = False
        Else
            Select Case Len(phone)
                Case 6, 10, 11 : getSmallPhone = phone
                Case Else : getSmallPhone = False
            End Select
        End If
    End Function



    Public Function getPhonesArray(ByVal str As String) As Object
        Dim temp As Object, temp2 As Object, n As Integer
        str = str.Replace("  ", " ")
        str = str.Replace("+7", "8")
        str = Regex.Replace(str, "(-|\+|\(|\))", "")
        temp = getSmallPhone(str)
        If temp <> False Then
            getPhonesArray = {getBigPhone(temp.ToString).ToString}
        Else
            str = str.Replace(", ", "|")
            str = str.Replace(",", "|")
            str = str.Replace(" ", "|")

            temp = str.Split("|")
            For n = 0 To UBound(temp)
                temp2 = getSmallPhone(temp(n))
                If temp2 <> False Then
                    temp(n) = getBigPhone(temp2)
                Else
                    temp(n) = Regex.Replace(temp(n), "(-|\s|\+|\(|\))", "")
                End If
            Next
            getPhonesArray = temp
        End If
    End Function



    Public Sub AdvertsNormalize(ByRef adv() As AdvertType)
        'Удаляем пустые объявления и риэлторов в БД
        Dim adverts2() As AdvertType
        Dim c As Integer
        c = 0
        For n = 0 To adv.Count - 1
            If adv(n).state = AdvState.normal Or adv(n).state = AdvState.estateNotDB Then
                ReDim Preserve adverts2(c)
                adverts2(c) = adv(n)
                c = c + 1
            End If
        Next
        adv = adverts2
        adverts2 = Nothing

        If IsNothing(adv) Then Exit Sub

        'Сортируем массив по улице
        'Exit Sub
        Dim swap As AdvertType
        For n = 0 To adv.Count - 2
            For m = n + 1 To adv.Count - 1
                If adv(n).street > adv(m).street Then
                    swap = adv(n)
                    adv(n) = adv(m)
                    adv(m) = swap
                End If
            Next
        Next
    End Sub



    'Обрабатывает каждый телефон, ищем кол-во совпадений номеров
    Public Sub AdvertsPhonesCount(ByRef adv() As AdvertType)
        Dim count As Integer
        Dim counter As New ArrayList

        If IsNothing(adv) Then Exit Sub

        'Устанавливаем размер массива phone_count равный размеру массива phone
        For n = 0 To UBound(adv)
            ReDim adv(n).phone_count(UBound(adv(n).phone))
        Next

        For n = 0 To UBound(adv)
            'ReDim adv(n).phone_count(UBound(adv(n).phone))
            For m = 0 To UBound(adv(n).phone)
                count = 0
                counter.Clear()
                For i = 0 To UBound(adv)
                    For j = 0 To UBound(adv(i).phone)
                        If adv(n).phone(m) = adv(i).phone(j) Then
                            count += 1
                            counter.Add({i, j})
                        End If
                    Next
                Next

                For z = 0 To counter.Count - 1
                    adv(counter(z)(0)).phone_count(counter(z)(1)) = count
                Next
            Next
        Next
    End Sub



    'Обрабатывает каждый телефон, и сохраняет общее количество телефонов
    Public Sub AdvertsPhonesCounter(ByRef adv() As AdvertType, ByRef adv_name As String)
        Dim phones As New ArrayList
        Dim n As Integer, m As Integer

        If IsNothing(adv) Then Exit Sub

        For n = 0 To UBound(adv)
            With adv(n)
                For m = 0 To UBound(.phone)
                    'Если в массиве еще нет телефона, то
                    If phones.Contains(.phone(m)) = False Then
                        'заносим телефон в массив
                        phones.Add(.phone(m))
                        'заносим телефон в таблицу счетчик
                        DBCounter_Inc(.phone(m), adv_name) 'для текущего сайта
                        DBCounter_Inc(.phone(m), "all")    'для всех сайтов
                    End If
                Next m
            End With
        Next n
    End Sub


    'Подготавливает список перед выводом
    Public Sub AdvertsCreateList(ByRef adv() As AdvertType, ByRef save_new As Boolean, ByRef adv_name As String)
        'Подаем сигнал
        Beep()

        'Удаляем пустые объявления и риэлторов в БД
        'Сортируем массив по улице, и номеру дома
        AdvertsNormalize(adv)

        'Обрабатываем каждый телефон, ищем кол-во совпадений номеров
        AdvertsPhonesCount(adv)

        'Если нужно сохранить новое объявление
        If save_new = True And Not IsNothing(adv) Then
            'Сохраняем новые объявления
            For n = 0 To adv.Count - 1
                If DBAdverts_isOldAdvert(adv_name, adv(n).advid) = False Then
                    DBAdverts_addOldAdvert(adv_name, adv(n).advid)
                End If
            Next
            'Сохраняем общее количество телефонов
            AdvertsPhonesCounter(adv, adv_name)
        End If
    End Sub



    'Собирает и выводит HTML код
    Public Sub AdvertsShowList(ByRef web As WebBrowser, ByRef adv() As AdvertType, ByRef h1 As String, ByVal eventHandler As System.EventHandler, ByRef adv_name As String)
        Dim html As String, tmpS As String
        Dim tpl_main As String, tpl_row As String, tpl_empty As String
        Dim parse As Object, rows As String = ""
        Dim n As Integer, m As Integer, i As Integer
        Dim col1 As String, col2 As String, col3 As String, col4 As String
        Dim elem As System.Windows.Forms.HtmlElement

        tpl_main = TplLoad("html.import.tpl", System.Text.Encoding.UTF8)
        tpl_row = TplLoad("html.import.row.tpl", System.Text.Encoding.UTF8)
        tpl_empty = TplLoad("html.import.empty.tpl", System.Text.Encoding.UTF8)

        If Not IsNothing(adv) Then
            For n = 0 To UBound(adv)
                parse = Nothing
                With adv(n)
                    'Текст
                    tmpS = IIf(.nomer <> "", ", " & .nomer & ", ", " ")
                    TplAddParse(parse, "text", .street & tmpS & .text)

                    col1 = "" : col2 = "" : col3 = "" : col4 = ""
                    For m = 0 To UBound(.phone)
                        'Собираем телефоны
                        col1 &= "<nobr>" & "<span class='phone'>" & .phone(m) & "</span></nobr>" & _
                            IIf(.state = AdvState.estateNotDB, " <b>(агентство)</b>", "")
                        If m < UBound(.phone) Then col1 &= "<br>"

                        'Собираем количество
                        i = CInt(.phone_count(m))
                        col2 &= IIf(i = 1, i, "<b>" & i & "</b>")
                        If m < UBound(.phone) Then col2 &= "<br>"

                        'Собираем общее количество для текущего сайта
                        col3 &= DBCounter_Get(.phone(m), adv_name)
                        If m < UBound(.phone) Then col3 &= "<br>"

                        'Собираем общее количество для всех сайтов
                        col4 &= DBCounter_Get(.phone(m), "all")
                        If m < UBound(.phone) Then col4 &= "<br>"
                    Next
                    TplAddParse(parse, "phones", col1)
                    TplAddParse(parse, "count", col2)
                    TplAddParse(parse, "counter_cur", col3)
                    TplAddParse(parse, "counter_all", col4)

                    'Парсим объект
                    rows &= TplParcer(tpl_row, parse)
                End With
            Next
        Else
            rows = tpl_empty
        End If

        'Парсим главный шаблон
        parse = Nothing
        TplAddParse(parse, "title", h1)
        TplAddParse(parse, "rows", rows)
        html = TplParcer(tpl_main, parse)

        'Обновляем WebBrowser
        web.DocumentText = html
        web.Update()
        ReadyStateComplete(web, 10000)

        'Назначаем событие click каждому элементу span
        For Each elem In web.Document.GetElementsByTagName("span")
            If elem.GetAttribute("className") = "phone" Then
                elem.AttachEventHandler("onclick", eventHandler)
            End If
        Next
    End Sub



    Public Sub AdvertsSpanPhoneClick(ByRef web As WebBrowser, ByRef adv() As AdvertType, ByRef h1 As String, ByVal eventHandler As System.EventHandler, ByRef adv_name As String)
        Dim phone As String
        Dim span As HtmlElement
        Dim n As Integer, m As Integer
        Dim phones As New ArrayList

        'Выберем телефоны в массив из ячейки
        For Each span In web.Document.ActiveElement.GetElementsByTagName("span")
            phones.Add(span.InnerText)
        Next

        'Запрашиваем у пользователя номера телефонов
        frmPhoneAdd.txtEstates.Text = Join(phones.ToArray(), vbCrLf)
        frmPhoneAdd.ShowDialog()
        If frmPhoneAdd.enter_phones = False Then Exit Sub

        phones.Clear()
        phones.AddRange(frmPhoneAdd.phones)

        'Заносим телефоны в базу данных
        DBEstate_AddEstate(phones)

        'Проходим по всем объявлениям, находим такие же телефоны и устанавливаем state=estateInDB
        For Each phone In phones
            phone = getBigPhone(phone)
            For n = 0 To UBound(adv)
                For m = 0 To UBound(adv(n).phone)
                    If adv(n).phone(m) = phone Then adv(n).state = AdvState.estateInDB
                Next
            Next
        Next

        'Удаляем пустые объявления и риэлторов в БД
        'Сортируем массив по улице
        AdvertsNormalize(adv)

        'Собираем HTML код и обновляем WebBrowser
        AdvertsShowList(web, adv, h1, eventHandler, adv_name)
    End Sub



    '========================================
    '========== РАБОТА С ШАБЛОНАМИ ==========
    '========================================

    'Загружает шаблон из файла
    Public Function TplLoad(ByVal tpl_file As String, ByVal encoding As System.Text.Encoding) As String
        Dim TplPath As String
        TplPath = Application.StartupPath & "\Resources\Tpls\" & tpl_file
        Return IO.File.ReadAllText(TplPath, encoding)
    End Function


    Public Sub TplAddParse(ByRef parse As Object, ByVal name As String, ByVal value As String)
        Dim c As Integer
        If IsNothing(parse) Then
            c = 0
            ReDim parse(1, c)
        Else
            c = UBound(parse, 2) + 1
            ReDim Preserve parse(1, c)
        End If
        parse(0, c) = name
        parse(1, c) = value
    End Sub


    Public Function TplParcer(ByVal tmpl As String, ByRef parse As Object) As String
        Dim n As Integer
        For n = 0 To UBound(parse, 2)
            tmpl = tmpl.Replace("%" & parse(0, n) & "%", parse(1, n))
        Next
        Return tmpl
    End Function



    Public Sub myConsole(ByRef lst As ListBox, ByVal value As String)
        lst.Items.Add(TimeString & "   " & value)
        lst.SelectedIndex = lst.Items.Count - 1
    End Sub



    Public Function GetNormStreet(ByVal street As String, ByVal isLeft As Boolean, ByVal repl() As String) As String
        Dim cur As String
        Dim trim_chars() As Char = {",", ".", " "}
        For Each cur In repl
            If isLeft = True Then
                If street.StartsWith(cur) = True Then street = street.Substring(cur.Length)
            Else
                If street.EndsWith(cur) = True Then street = street.Substring(0, street.Length - cur.Length)
            End If
        Next
        street = street.Trim(trim_chars)
        'Меняем первую букву в верхний и остальные в нижний регистр
        If street.Length > 1 Then
            street = Left(street, 1).ToUpper() & Right(street, street.Length - 1).ToLower()
        End If
        Return street
    End Function



    'Работа с INI файлами
    Private Declare Function GetPrivateProfileStringA Lib "kernel32" (ByVal lpApplicationName As String, ByVal lpKeyName As String, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Integer, ByVal lpFileName As String) As Integer
    Private Declare Function WritePrivateProfileStringA Lib "kernel32" (ByVal lpApplicationName As String, ByVal lpKeyName As String, ByVal lpString As String, ByVal lpFileName As String) As Integer

    Public Function GetINIString(ByVal AppName As String, ByVal KeyName As String, Optional ByVal DefaultValue As String = "") As String
        Dim rc As String = Strings.StrDup(255, vbNullChar)
        Dim x As Integer
        x = GetPrivateProfileStringA(AppName, KeyName, DefaultValue, rc, 255, Application.StartupPath & "\setting.ini")
        Return IIf(x <> 0, Strings.Left(rc, x), DefaultValue)
        'Call WritePrivateProfileString("Main", "license", license, Application.StartupPath & "\setting.ini")
    End Function



    Public Sub SaveContent(ByRef content, ByVal filename)
        IO.File.WriteAllText(Application.StartupPath & "\" & filename, content, System.Text.Encoding.Default)
    End Sub


    'Возвращает исходную строку кодированную Base64 алгоритмом
    Public Function FromBase64String(ByVal base64 As String) As String
        If base64 Is Nothing Then Throw New ArgumentNullException("base64")
        Return System.Text.Encoding.UTF8.GetString( _
            Convert.FromBase64String(base64) _
        )
    End Function


    'Возвращает MD5 hash строки
    Public Function ToMD5String(ByVal strToHash As String) As String
        Dim md5Obj As New Security.Cryptography.MD5CryptoServiceProvider
        Dim ByteHash() As Byte = md5Obj.ComputeHash(System.Text.Encoding.Default.GetBytes(strToHash))
        Dim strResult As String = ""
        For Each b As Byte In ByteHash
            strResult += b.ToString("x2")
        Next
        Return strResult
    End Function
End Module